import os
import io
import subprocess

from rcfg import RCfg
from pathlib import Path
from rdb import Rdb
from rui import RUi
from rich.prompt import Prompt
from rich.tree import Tree
from rich import print
from rgit import RGit
from rftp import RFtp
from util import vim_edit, combine_path
import shutil
import time
from rich.live import Live
from rich.table import Table


def generate_table(p_m_t_a) -> Table:
    """Make a new table."""
    table = Table()
    table.add_column("Model")
    table.add_column("Status")
    table.add_column("Time")

    for p_m_t in p_m_t_a:
        m = p_m_t[1].model()
        p = p_m_t[0]  # still running
        if isinstance(p, str):
            tm = p_m_t[2]
            msg = p_m_t[0]
        else:
            s = p.poll()
            tm = '{:.2f}'.format(time.time() - p_m_t[2])
            if s is None:
                msg = "[yellow]Running"
            elif s == 0:
                msg = "[green]Pass"
                p_m_t[0] = msg
                p_m_t[2] = tm
            else:
                msg = "[red]Fail"
                p_m_t[0] = msg
                p_m_t[2] = tm

        table.add_row(
            f"{m}", msg, tm
        )
    return table


class RCtl:
    def __init__(self, cfg: RCfg):
        self._cfg = cfg
        self._ui = RUi()
        self._dbs = []
        self._init_dbs()
        self._cfg.load()
        self._new_feature_for_all = ''
        self._bug_fix_for_all = ''

    def _init_dbs(self):
        self._dbs = []
        for d in self._cfg.rls_dir_list():
            for m in Path(d).iterdir():
                if Rdb.is_model(m):
                    self._dbs.append(Rdb(
                        m, var_dict=None,
                        cfg=self._cfg, ui=self._ui
                    ))

    def update_dbs(self):
        self._init_dbs()

    def pop_man(self):
        d = ['Set release config', 'Set global config',
             'Save config to disk', 'Start releasing',
             'Show release info', 'Auto make', 'Auto make(core fw.all)',
             'Auto make(with clean)', 'Quit']
        opt = self._ui.ui_select(d, 'Menu')
        while opt != 'Quit':
            if opt == 'Set release config':
                self.pop_models()
            elif opt == 'Set global config':
                self.pop_config()
            elif opt == 'Save config to disk':
                self._cfg.update(w=True)
            elif opt == 'Start releasing':
                # TODO releasing!!!!!
                self.releasing()
            elif opt == 'Auto make':
                self._ui.cls()
                p_m_t_a = [[
                    db.make_all(db.log_file_path()), db, time.time()
                ] for db in self._dbs]
                with Live(
                        generate_table(p_m_t_a), refresh_per_second=4
                ) as live:
                    s = [m[0].poll() for m in p_m_t_a if isinstance(m[0], subprocess.Popen)]
                    while None in s:
                        time.sleep(0.4)
                        live.update(generate_table(p_m_t_a))
                        s = [m[0].poll() for m in p_m_t_a if
                             isinstance(m[0], subprocess.Popen)]

                for p in p_m_t_a:
                    if isinstance(p[0], str) and 'Fail' in p[0]:
                        self._ui.ui_print_info(
                            f'Please see log file: {p[1].log_file_path()} for detail')
                input('Press any key to continue...')
            elif opt == 'Auto make(core fw.all)':
                self._ui.cls()
                p_m_t_a = [[
                    db.make_core_fw_all(db.log_file_path()), db, time.time()
                ] for db in self._dbs]
                with Live(
                        generate_table(p_m_t_a), refresh_per_second=4
                ) as live:
                    s = [m[0].poll() for m in p_m_t_a if isinstance(m[0], subprocess.Popen)]
                    while None in s:
                        time.sleep(0.4)
                        live.update(generate_table(p_m_t_a))
                        s = [m[0].poll() for m in p_m_t_a if
                             isinstance(m[0], subprocess.Popen)]
                for p in p_m_t_a:
                    if isinstance(p[0], str) and 'Fail' in p[0]:
                        self._ui.ui_print_info(
                            f'Please see log file: {p[1].log_file_path()} for detail')
                input('Press any key to continue...')
            elif opt == 'Auto make(with clean)':
                self._ui.cls()
                p_m_t_a = [[
                    db.make_core_clean_all(db.log_file_path()), db, time.time()
                ] for db in self._dbs]
                with Live(
                        generate_table(p_m_t_a), refresh_per_second=4
                ) as live:
                    s = [m[0].poll() for m in p_m_t_a if isinstance(m[0], subprocess.Popen)]
                    while None in s:
                        time.sleep(0.4)
                        live.update(generate_table(p_m_t_a))
                        s = [m[0].poll() for m in p_m_t_a if
                             isinstance(m[0], subprocess.Popen)]
                for p in p_m_t_a:
                    if isinstance(p[0], str) and 'Fail' in p[0]:
                        self._ui.ui_print_info(
                            f'Please see log file: {p[1].log_file_path()} for detail')
                input('Press any key to continue...')
            elif opt == 'Show release info':
                # TODO: show release infomation
                ...
            opt = self._ui.ui_select(d, 'Menu')
        exit(0)

    def gen_release_note(self, db: Rdb) -> []:
        last_release_note = db.ftp_db_last_release()
        # if self._cfg.ftp_fw() not in last_release_note:
        #     with open(last_release_note, 'r') as f:
        #         content = f.readlines()
        # else:
        ftp = RFtp(self._cfg.ftp_host())
        ftp.login(self._cfg.ftp_user(), self._cfg.ftp_passwd())
        dir = os.path.dirname(last_release_note)
        nm = last_release_note.split('/')[-1]
        buf = io.BytesIO()
        ftp.download_file_to_buf(buf, dir, nm)
        buf.seek(0)
        content = [i.decode() for i in buf.readlines()]
        ftp.close()

        head = content[0:10]
        history = content[10:]
        return head + db.release_note() + history

    def releasing(self):
        tr = Tree('Upload')
        for db in self._dbs:
            db.init_gitinfo_inmake()
            db.gen_checksum()
            model_release_to = combine_path([
                self._cfg.release_to(),
                db.model(),
                db.iss_sw_ver().split('-')[-1],
            ])
            local_hex_path = Path(combine_path([
                model_release_to,
                'hex'
            ]))
            local_all_hex_path = Path(combine_path([
                model_release_to,
                'all_hex'
            ]))
            local_backup_path = Path(combine_path([
                model_release_to,
                'backup'
            ]))
            local_release_note = combine_path([
                local_hex_path.__str__(),
                db.hex_prefix() + Rdb.release_note_tail
            ])
            local_hex_path.mkdir(parents=True, exist_ok=True)
            local_all_hex_path.mkdir(parents=True, exist_ok=True)
            local_backup_path.mkdir(parents=True, exist_ok=True)
            shutil.copy(db.hex(), local_hex_path)
            shutil.copy(db.cmds(), combine_path([
                local_hex_path.__str__(),
                db.hex_prefix() + Rdb.cmds_tail
            ]))
            shutil.copy(db.mib(), local_hex_path)
            shutil.copy(db.all_hex(), local_all_hex_path)
            shutil.copy(db.bin(), local_backup_path)
            shutil.copy(db.iss(), local_backup_path)
            with open(local_release_note, 'w') as f:
                f.writelines(self.gen_release_note(db))

            db.set_local_hex_path(local_hex_path.__str__())
            db.set_local_hex_all_path(local_all_hex_path.__str__())
            db.set_local_backup_path(local_backup_path.__str__())

            root = tr.add(db.model())
            for i in Path(local_hex_path.__str__()).iterdir():
                root.add(i.__str__()).add('---> ' + db.ftp_db_hex())
            for i in Path(local_all_hex_path.__str__()).iterdir():
                root.add(i.__str__()).add('---> ' + db.ftp_db_all_hex())
            for i in Path(local_backup_path.__str__()).iterdir():
                root.add(i.__str__()).add('---> ' + db.ftp_db_backup())

        self._ui.cls()
        print(tr)
        out = Prompt.ask('Confirm to upload?(Y/N)', default='Y')
        while out not in ('Y', 'N'):
            print(tr)
            out = Prompt.ask('Confirm to upload?(Y/N)(default:Y)', default='Y')

        if out == 'Y':
            for db in self._dbs:
                self.upload(db)  # TODO: uncomment me to Upload >_<
                ...
        self._ui.cls()

    def upload(self, db: Rdb):
        ftp = RFtp(self._cfg.ftp_host())
        ftp.login(self._cfg.ftp_user(), self._cfg.ftp_passwd())
        for i in Path(db.local_hex_path()).iterdir():
            print(str(i).split('/')[-1])
            nm = str(i).split('/')[-1]
            try:
                ftp.upload_file(db.local_hex_path(), db.ftp_db_hex(), nm)
            except:
                print('%s upload failed.\n'%(db.local_hex_path()))
        for i in Path(db.local_hex_all_path()).iterdir():
            print(str(i).split('/')[-1])
            nm = str(i).split('/')[-1]
            try:
                ftp.upload_file(db.local_hex_all_path(), db.ftp_db_all_hex(), nm)
            except:
                print('%s upload failed.\n'%(db.local_hex_all_path()))
        for i in Path(db.local_backup_path()).iterdir():
            print(str(i).split('/')[-1])
            nm = str(i).split('/')[-1]
            try:
                ftp.upload_file(db.local_backup_path(), db.ftp_db_backup(), nm)
            except:
                print('%s upload failed.\n'%(db.local_backup_path()))
        ftp.close()

    def pop_config(self):
        bak_release_dir = self._cfg.release_dir()
        var = self._cfg.dump()
        c = self._ui.ui_select(var, 'Select a config item to modify')
        while c:
            text = 'Enter ' + c
            out = Prompt.ask(text, default=var[c])
            while not RCfg.var_chk(c, out):
                warn = '[bold red]Invalid value[/bold red] ' + text
                out = Prompt.ask(warn, default=var[c])
            var[c] = out
            c = self._ui.ui_select(var, 'select a config item to modify')

        self._cfg.update(var, w=False)
        if self._cfg.release_dir() != bak_release_dir:
            self.update_dbs()

    def model2db(self, model: str):
        for d in self._dbs:
            if d.model() == model:
                return d
        return None

    def pop_models(self):
        d = {}
        for db in self._dbs:
            d[db.model()] = db.root()
        d['Set new feature for all models'] = ''
        d['Set bug fix for all models'] = ''
        d['Auto detect ftp path for all models'] = ''
        m = self._ui.ui_select(d, 'Select a model to config')
        while m:
            if m == 'Set new feature for all models':
                self._new_feature_for_all = vim_edit(self._new_feature_for_all)
                for db in self._dbs:
                    db.set_new_feature(self._new_feature_for_all)
            elif m == 'Set bug fix for all models':
                self._bug_fix_for_all = vim_edit(self._bug_fix_for_all)
                for db in self._dbs:
                    db.set_bug_fix(self._bug_fix_for_all)
            elif m == 'Auto detect ftp path for all models':
                for db in self._dbs:
                    db.sync_ftp_dirs()
            else:
                self.pop_model_options(self.model2db(m))
            m = self._ui.ui_select(d, 'Select a model to config')

    def pop_model_options(self, db: Rdb):
        d = ['Branches Setting', 'Ftp Setting', 'Auto detect ftp path',
             'New feature', 'Bug fix']
        opt = self._ui.ui_select(d, 'Select a setting to config')
        while opt:
            if opt == 'Branches Setting':
                repo_dict = db.cur_repo()
                repo = self._ui.ui_select(repo_dict, db.model())
                while repo:
                    br_dict = db.remote_br(repo)
                    br = self._ui.ui_select(br_dict, db.model() + '->' + repo)
                    while br:
                        RGit(db.repo(repo)).fetch_remote_branch(br)
                        br = self._ui.ui_select(br_dict, db.model() + '->' +
                                                repo)
                    repo_dict = db.cur_repo()
                    repo = self._ui.ui_select(repo_dict, db.model())
                db.update_release_info()
            elif opt == 'Ftp Setting':
                ftp_db = db.ftp_db()
                p = self._ui.ui_select(ftp_db, db.model())
                while p:
                    # TODO ********
                    text = 'Enter ' + p
                    out = Prompt.ask(text, default=ftp_db[p])
                    while not db.ftp_db_var_check(p, out):
                        warn = '[bold red]Invalid value[/bold red] ' + text
                        out = Prompt.ask(warn, default=ftp_db[p])
                    ftp_db[p] = out
                    ftp_db = db.ftp_db()
                    p = self._ui.ui_select(ftp_db, db.model())
            elif opt == 'Auto detect ftp path':
                db.sync_ftp_dirs()
            elif opt == 'New feature':
                db.set_new_feature(vim_edit(db.new_feature()))
            elif opt == 'Bug fix':
                db.set_bug_fix(vim_edit(db.bug_fix()))
            opt = self._ui.ui_select(d, 'Select a setting to config')
